<!--
	签名组件
    案例：添加标签<qm-sign ref="signSheet"></qm-sign>
    调用签名：this.$refs.signSheet.openFaceSign(option);
    当签名点击确定后将会调用saveSignCallback方法
    
		props
			title[string => ''] 标题
			resignClick[Function => undefined] 点击“重签”时，需要调用的方法。必选，必选，必选
			saveSignCallback[Function => undefined] 点击“确定”且我签名成功时，需要调用的方法。必选，必选，必选
			saveFaceSignCallback[Function => undefined] 人脸签名成功时，调用的方法

    通过 refs 访问或调用数据请查看data()、methods
-->
<template>
  <f7-sheet class="sign-popup" style="height: 100%; background: #cfd5da" @sheet:close="onSheetClose">
    <f7-page-content :placeholder="title">
      <div class="sign-block">
        <sign-canvas
          class="sign-canvas"
          ref="SignCanvas"
          :options="signOptions"
          v-model="signValue"
        />
      </div>
      <f7-toolbar class="sign-toolbar">
        <div class="right">
          <f7-button fill sheet-close color="gray" class="v-sign-btn">
            关　闭
          </f7-button>
        </div>
        <div class="center">
          <f7-button fill @click="saveSign(false)" class="v-sign-btn">
            确　定
          </f7-button>
        </div>
        <div class="center" v-show="showNextBtn">
          <f7-button
            fill
            @click="saveSign(true)"
            sheet-close
            class="v-sign-btn"
          >
            下一个
          </f7-button>
        </div>
        <div class="center" v-show="showResignBtn">
          <f7-button fill @click="resignClick()" class="v-sign-btn">
            重　签
          </f7-button>
        </div>
        <div class="left">
          <f7-button
            fill
            @click="clearCanvasClick"
            color="red"
            class="v-sign-btn"
          >
            清　空
          </f7-button>
        </div>
      </f7-toolbar>
        <f7-actions ref="actionType">
            <f7-actions-group>
<!--                <f7-actions-button @click="openFaceSign">人脸签名</f7-actions-button>-->
                <f7-actions-button @click="openSign">手写签名</f7-actions-button>
                <f7-actions-button color="red" @click="$f7.sheet.close('.sign-popup')">取消</f7-actions-button>
            </f7-actions-group>
        </f7-actions>
    </f7-page-content>
  </f7-sheet>
</template>

<script>
import SignCanvas from "sign-canvas";
export default {
  components: { SignCanvas },
  props: {
    /** sheet 标题。可选 */
    title: {
      type: String,
      default: "签名"
    },
    /** 重签，清除现有签名，重新签名。必选参数 */
    resignClick: {
      type: Function,
      default: undefined
    },
    /** 签名成功后的回调函数。必选参数
     * @param {Object} signImage 签名后的图片数据，base64
     */
    saveSignCallback: {
      type: Function,
      default: undefined
    },
    /** 人脸签名成功后的回调函数。可选参数
     * @param {Object} signImage 签名后的图片数据，base64
     */
    saveFaceSignCallback: {
      type: Function,
      default: () => {}
    },
    /** 保存签名的链接。可选 */
    saveUrl: {
      type: String,
      default: "/api/signature/signature/save"
    },
    /** 获取签名的链接。可选 */
    getUrl: {
      type: String,
      default: "/api/signature/signature/getSignature"
    },
    /** 锁定屏幕的方向，默认正向竖屏；top、right、buttom、left；详情参考util.lockOrientation */
    lockOrientation: {
      type: String,
      default: "top"
    },
  },
  data() {
    return {
      /** 是否人脸签名 */
      isFaceSignTmp: false,
      /** 当前签名数据 */
      signValue: null,
      /** 是否显示“重签”按钮。可选 */
      showResignBtn: true,
      /** 是否显示“下一个”按钮。可选 */
      showNextBtn: true,
      /** 签名配置 */
      signOptions: {
        /** canvas宽高 [Number] 可选 */
        canvasHeight: document.documentElement.clientHeight - 115,
        /** 高度  [Number] 可选 */
        canvasWidth: document.documentElement.clientWidth - 20,
        /** 画笔大小 */
        writeWidth: 13,
        /** 签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框 */
        isSign: true,
        /** 是否显示边框 [可选] */
        isShowBorder: false,
        /** 背景色 */
        bgColor: "rgba(0, 0, 0, 0)",
      },
      /** 用户自定义参数临时变量 */
      userOptions: null,
      signType: null
    };
  },
  created(){
    var docEl = document.documentElement;
    var signOptions = this.signOptions;
    signOptions.canvasHeight = docEl.clientHeight;
    signOptions.canvasWidth = docEl.clientWidth;
    if(docEl.clientHeight < docEl.clientWidth){
      signOptions.canvasHeight = docEl.clientWidth;
      signOptions.canvasWidth = docEl.clientHeight;
    }
    signOptions.canvasHeight -= 115;//减按钮高度
    signOptions.canvasWidth -= 20;//减左右边距
  },
  methods: {
    /**
     * 弹出签名对话框
     * @param {Object} options 配置参数
     */
    openAction(options){
      if(this.isPlus()){
        var showActionBtn = false;
        if(options){
          showActionBtn = options.showActionBtn == true;
        }
        if(showActionBtn){
          this.$refs.actionType.f7Actions.open();
          return;
        }
      }

      this.openSign(options);
    },
    /**
     * 人脸签名
     * @param {Object} options 配置参数
     */
    openFaceSign(options) {
      var self = this;
      var isView = false;
      if (options) {
        isView = options.isView == true;
      }
      if (isView) {
        return;
      }
      self.userOptions = options;

      if (!window.plus) {
        self.isFaceSignTmp = false;
        self.openSignPopup(options);
        return;
      }

      plus.aiFace.detect(
        { type: "search" },
        (re) => {
          self.isFaceSignTmp = true;
          if (!re || re.success != true) {
            self.$f7.dialog.alert(
              re.message || "未匹到人脸信息，请先登录账号并人脸认证"
            );
            return;
          }

          var faceUserId = re.userId;
          if (faceUserId && faceUserId.indexOf("_") != -1) {
            faceUserId = faceUserId.replace(/_/g, "-");
          }
          var postData = { userId: faceUserId };
          var url = self.getUrl;
          self.$f7.request.postJSON(url, postData, (response) => {
            if (!self.$util.isBoolean(response?.success)) {
              var msg = "服务异常";
              if (response?.message) {
                msg = response.message;
              }
              self.$f7.dialog.alert(msg);
              return;
            }

            if (response.success != true) {
              if (self.userId != faceUserId) {
                var msg =
                  "人脸信息与当前登录用户不一致！人脸主人尚未签名，请先登录账号在我的签名处签名后，再试";
                self.$f7.dialog.alert(msg);
                return;
              }

              self.faceingUserId = faceUserId;
              self.openSignPopup(options);
              return;
            }

            if (self.$util.isFunction(self.saveFaceSignCallback)) {
              self.saveFaceSignCallback(response.parameters);
            }
          });
        },
        (e) => {
          if (e && (e.code == 11000 || e.code == 222207 || e.code == 223106)) {
            self.$f7.dialog.alert("未匹到人脸信息，请先登录账号并人脸认证");
          } else {
            self.$f7.dialog.alert(e.message || "刷脸登录失败，请稍后重试");
          }
        }
      );
    },
    /**
     * 普通签名
     * @param {Object} options 参数，可选
     */
    openSign(options) {
      var self = this;
      var isView = false;
      if (options) {
        isView = options.isView == true;
      }
      if (isView) {
        return;
      }

      self.isFaceSignTmp = false;
      self.userOptions = options;
      self.openSignPopup(options);
    },
    /**
     * 打开签名画板
     * @param {Object} options 参数，可选，{
       *                   isView: [Boolean => undefined] 是否为查看模式，当设置为true时，将不会弹出签名画板；
       * }
     */
    openSignPopup(options) {
      var self = this;
      var isView = false;
      if (options) {
        isView = options.isView == true;
      }
      if (isView) {
        return;
      }

      self.userOptions = options;
      self.$refs.SignCanvas.canvasClear();
      self.$f7.sheet.open(".sign-popup");
      setTimeout(() => {
        self.$util.lockOrientation(self.lockOrientation);
      }, 10);
    },
    /**
     * 保存签名
     * @param {Boolean} isNext 是否保存并继续
     */
    saveSign(isNext) {
      var self = this;
      if (!self.userOptions) {
        self.userOptions = {};
      }

      const img = self.$refs.SignCanvas.saveAsImg();
      self.rotateBase64Img(img, -90, async (nimg) => {
        if (self.isCanvasBlank(nimg) == true) {
          self.faceingUserId = undefined;
          return false;
        }
        try {
          await self.addWaterMarker(nimg);
        } catch (e) {
          console.error(e, "addWaterMarker");
        }
        nimg = nimg.toDataURL("image/png");

        if (self.faceingUserId) {
          self.$f7.request.postJSON(self.saveUrl, {
            signImg: nimg,
            userId: self.faceingUserId,
          });
          self.faceingUserId = undefined;
        }

        if (self.$util.isFunction(self.saveSignCallback)) {
          self.saveSignCallback(nimg, isNext);
        }
      });

      if (isNext !== true) {
        self.$f7.sheet.close(".sign-popup");
        return;
      }

      if (self.isFaceSignTmp) {
        self.openFaceSign(self.userOptions);
      } else {
        self.openSignPopup(self.userOptions);
      }
    },
    /**
     * 保存签名
     * @param {Boolean} signImg 签名数据，base64，必选参数
     * @param {Boolean} userId 用户Id，可选参数
     */
    doSaveSign(signImg, userId, successCallbackFunc) {
      var self = this;
      if (!signImg) {
        self.$f7.dialog.alert("请签名后，再尝试保存");
        return;
      }

      var postData = { signImg: signImg, userId: userId };
      self.$f7.preloader.show();
      self.$f7.request.postJSON(self.saveUrl, postData, (r) => {
        self.$f7.preloader.hide();
        if (r?.success) {
          if (self.$util.isFunction(successCallbackFunc)) {
            successCallbackFunc();
          }
          self.$f7.dialog.alert("保存成功");
        } else {
          self.$f7.dialog.alert(r?.message || "保存失败，请稍后再试");
        }
      });
    },
    isShowReSignBtn: function (isShow) {
      this.showResignBtn = isShow;
    },
    isShowNextBtn: function (isShow) {
      this.showNextBtn = isShow;
    },
    /** 清空画布 */
    clearCanvasClick() {
      this.$refs.SignCanvas.canvasClear();
    },
    /** 图片旋转
     * @param {*} src 图片
     * @param {*} edg 旋转角度，必须是90的倍数
     * @param {Function} callbackFunc 回调函数
     */
    rotateBase64Img(src, edg, callbackFunc) {
      var canvas = document.createElement("canvas");
      var ctx = canvas.getContext("2d");

      var imgW; //图片宽度
      var imgH; //图片高度
      var size; //canvas初始大小

      if (edg % 90 != 0) {
        console.error("旋转角度必须是90的倍数!");
        throw "旋转角度必须是90的倍数!";
      }
      edg < 0 && (edg = (edg % 360) + 360);
      const quadrant = (edg / 90) % 4; //旋转象限
      const cutCoor = { sx: 0, sy: 0, ex: 0, ey: 0 }; //裁剪坐标

      var image = new Image();
      image.crossOrigin = "anonymous";
      image.src = src;

      var self = this;
      image.onload = () => {
        imgW = image.width;
        imgH = image.height;
        size = imgW > imgH ? imgW : imgH;

        canvas.width = size * 2;
        canvas.height = size * 2;
        switch (quadrant) {
          case 0:
            cutCoor.sx = size;
            cutCoor.sy = size;
            cutCoor.ex = size + imgW;
            cutCoor.ey = size + imgH;
            break;
          case 1:
            cutCoor.sx = size - imgH;
            cutCoor.sy = size;
            cutCoor.ex = size;
            cutCoor.ey = size + imgW;
            break;
          case 2:
            cutCoor.sx = size - imgW;
            cutCoor.sy = size - imgH;
            cutCoor.ex = size;
            cutCoor.ey = size;
            break;
          case 3:
            cutCoor.sx = size;
            cutCoor.sy = size - imgW;
            cutCoor.ex = size + imgH;
            cutCoor.ey = size + imgW;
            break;
        }

        ctx.translate(size, size);
        ctx.rotate((edg * Math.PI) / 180);
        ctx.drawImage(image, 0, 0);

        var imgData = ctx.getImageData(
          cutCoor.sx,
          cutCoor.sy,
          cutCoor.ex,
          cutCoor.ey
        );
        if (quadrant % 2 == 0) {
          canvas.width = imgW;
          canvas.height = imgH;
        } else {
          canvas.width = imgH;
          canvas.height = imgW;
        }
        ctx.putImageData(imgData, 0, 0);

        self.$util.isFunction(callbackFunc) && callbackFunc(canvas);
      };
    },
    /** 判断画布是否存在内容
     * @param {*} canvas 画布
     */
    isCanvasBlank(canvas) {
      let blank = document.createElement("canvas"); //系统获取一个空canvas对象
      blank.width = canvas.width;
      blank.height = canvas.height;
      return canvas.toDataURL() == blank.toDataURL(); //比较值相等则为空
    },
    /** 添加水印
     * @param {*} canvas 画布
     */
    async addWaterMarker(canvas) {
      var self = this;
      const config = self.$framework.config;
      if(config.setting.enableSignWaterMarker!=true){
        return;
      }
      
      const marker = config.setting.signWaterMarker;
      let density = config.setting.signWaterDensity;
      let alpha = config.setting.signWaterAlpha;
      if (!marker) {
        return;
      }

      var cans = canvas.getContext("2d");
      cans.textAlign = "left";
      cans.textBaseline = "Middle";

      if (marker.startsWith("/static/")) {
        const img = await this.loadImage(marker);
        if (!img) {
          return;
        }

        density = density || 6;
        alpha = alpha || 0.1;
        let fwidth = canvas.width / density;
        let fheight = canvas.height / density;
        for (
          let hh = fheight / (density / 2);
          hh < canvas.height;
          hh += fheight
        ) {
          for (
            let ww = fwidth / (density / 2);
            ww < canvas.width;
            ww += fwidth
          ) {
            cans.globalAlpha = 0.1;
            cans.drawImage(img, ww, hh, 30, 30);
          }
        }
        return;
      }
      
      density = density || 4;
      alpha = alpha || 0.8;
      let fwidth = canvas.width / density;
      let fheight = canvas.height / density;
      cans.font = "16px Microsoft JhengHei";
      cans.fillStyle = "rgba(180, 180, 180, " + alpha + ")";
      cans.rotate((-20 * Math.PI) / 180);
      for (
        let hh = -1 * canvas.height;
        hh < canvas.height * 2;
        hh += fheight
      ) {
        for (
          let ww = -1 * canvas.width;
          ww < canvas.width * 2;
          ww += fwidth
        ) {
          cans.fillText(marker, ww, hh);
        }
      }
    },
    /** 从指定路径中加载图片
     * @param {*} src 图片路径
     */
    loadImage(src) {
      if (window.plus) {
        src = src.substring(1);
      }
      return new Promise((resolve, reject) => {
        let img = new Image();
        img.crossOrigin = "*";
        img.onload = () => {
          resolve(img);
        };
        img.onerror = () => {
          reject(new Error("图片解析失败"));
        };
        img.src = src;
      });
    },
    isPlus(){
      return window.plus?true:false;
    },
    onSheetClose(){
      if(this.$util.isPad()){
        this.$util.unlockOrientation();
      }
    }
  },
};
</script>